/**
 * Copyright (c) 2009 Cycle Information Technology
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this 
 * software and associated documentation files (the "Software"), to deal in the Software 
 * without restriction, including without limitation the rights to use, copy, modify, 
 * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 
 * permit persons to whom the Software is furnished to do so, subject to the following 
 * conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all copies 
 * or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 
 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 * 
 * 
 */
package com.cycleit.cycleframework.controller{
	import com.cycleit.cycleframework.entity.ServiceInfo;
	import com.cycleit.cycleframework.exception.CycleException;
	
	import flash.utils.Dictionary;
	
	import mx.binding.utils.ChangeWatcher;
	import mx.events.PropertyChangeEvent;
	
	/**
	 * 
	 * Controlador dedicado al refresco de datos, para una determinada variable, a&#241;adiremos un
 	 * watcher para escuchar los cambios y disparar funciones para que nos refresquen el modelo.
	 * 
	 * @see mx.binding.utils.ChangeWatcher
	 * @see mx.events.PropertyChangeEvent
	 */ 
	public class ServicesController  {
		/**
		 * Diccionario de mapeos entre watcher y navegaciones.
		 */  
		protected var watchers : Dictionary = new Dictionary();
		
		public function ServicesController() {
		  
		}
		
		/**
		 * Añade un watcher a variables del modelo y asocia a los cambios
		 * en estas con la ejecución de métodos de refresco.
		 *  
		 * @param host Fuente de datos donde se encuentra la variable que queremos observar frente a cambios que sufra.
		 * @param property Propiedad de la fuente de datos que queremos observar.
		 * @param service Función a ejecutar cuando cambie <code>property</code>.
		 * 
		 * @throws com.cycleit.cycleframework.exception.CycleException Cuando no se especifica la fuente de datos.
		 * @throws com.cycleit.cycleframework.exception.CycleException Cuando no se especifica la propiedad.
		 * @throws com.cycleit.cycleframework.exception.CycleException Cuando no se especifica la función a ejecutar ni un manejador.
		 * @throws com.cycleit.cycleframework.exception.CycleException Cuando ya se ha añadido un watcher a esa propiedad.
		 */		
		public function addWatcher(host:Object, property:String, service:Function):void {
			if (host == null) { 
				throw new CycleException(CycleException.NEED_HOST);
			}
			    
			if (property == null) {
				throw new CycleException(CycleException.NEED_PROPERTY);
			}
			 
			if (service == null) {
				throw new CycleException(CycleException.NEED_SERVICE);
			}
			 
			var id:String = host.toString()+property;
			 
			if(watchers[id] == null) {
				var cw:ChangeWatcher = ChangeWatcher.watch(host,property,genericExecutionServiceHandler); 
				watchers[id] = new ServiceInfo(service,cw);
			}else{
				throw new CycleException( CycleException.SERVICE_ALREADY_EXIST, id );
			}
		}
		
		/**
		 * Añade un watcher a variables del modelo y asocia a los cambios
		 * en estas con la ejecución de métodos de refresco.
		 *  
		 * @param host Fuente de datos donde se encuentra la variable que queremos observar frente a cambios que sufra.
		 * @param property Propiedad de la fuente de datos que queremos observar.
		 * @param handler Manejador a medida para los cambios de <code>property</code>.
		 * 
		 * @throws com.cycleit.cycleframework.exception.CycleException Cuando no se especifica la fuente de datos.
		 * @throws com.cycleit.cycleframework.exception.CycleException Cuando no se especifica la propiedad.
		 * @throws com.cycleit.cycleframework.exception.CycleException Cuando no se especifica la función a ejecutar ni un manejador.
		 * @throws com.cycleit.cycleframework.exception.CycleException Cuando ya se ha añadido un watcher a esa propiedad.
		 */		
		public function addHandler(host:Object, property:String, handler:Function):void {
			if (host == null) { 
				throw new CycleException(CycleException.NEED_HOST);
			}
			    
			if (property == null) {
				throw new CycleException(CycleException.NEED_PROPERTY);
			}
			 
			if (handler == null) {
				throw new CycleException(CycleException.NEED_HANDLER);
			}
			 
			var id:String = host.toString()+property;
			 
			if(watchers[id] == null) {
				var cw:ChangeWatcher = ChangeWatcher.watch(host,property,handler); 
				watchers[id] = new ServiceInfo(null,cw);
			}else{
				throw new CycleException( CycleException.SERVICE_ALREADY_EXIST, id );
			}
		}
		
		/**
		 * Elimina los watcher asociado a una fuente de datos y una propiedad de la misma.
		 *  
		 * @param host Fuente de datos
		 * @param property Propiedad inspeccionada
		 * 
		 */		
		public function removeWatcher( host:Object, property:String):void {
			var id:String = host.toString()+property;
			var nInfo:ServiceInfo = watchers[id] as ServiceInfo;
			if(nInfo){
				nInfo.watcher.unwatch();
				watchers[id] = undefined;
			 	delete watchers[id]; 
			}
		}
         
 
		/**
		 * De forma genérica si introducimos una función de refresco, que como entrada tiene los datos del 
		 * tipo de la variable que se esta escuchando, esta se introduce automáticamente como parametro de  
		 * entrada de la función de refresco.
		 * 
		 * @param event Evento despachado cuando la propiedad dentro de la fuente de datos cambia.
		 * 
		 */
		protected function genericExecutionServiceHandler(event:PropertyChangeEvent):void {
			var id:String = event.source.toString()+event.property;
			var sInfo:ServiceInfo = watchers[id] as ServiceInfo;
			if(sInfo){
				sInfo.service(event.newValue);
			}    
		}
		  
		/**
		 * Devuelve un ServiceInfo en función del identificador de watcher que se haya introducido.
		 *  
		 * @param id Identificador del watcher que queremos buscar.
		 * @return ServiceInfo asociado al identificador del watcher introducido.
		 * 
		 * @throws com.cycleit.cycleframework.exception.CycleException El identificador introducido no está asociado a ningún watcher.
		 */		
		protected function getWatcher( id : String ) : ServiceInfo {
			var watcher : ServiceInfo = watchers[ id ];
			 
			if( watcher == null ){
				throw new CycleException( CycleException.SERVICE_NOT_FOUND, id );
			}
			return watcher;
		}
	}   
}